#include<iostream>
#include<cassert>
#include<cstring>
#include<sys/types.h>
#include<sys/wait.h>
#include<cstdlib>
#include<vector>
#include<string>
#include<unistd.h>
#include<time.h>
#include<cstdio>

#define NUM 5 //要创建的子进程数

typedef void(*Task)(); //要执行的任务的函数指针

//任务列表
void task1()
{
    std::cout<<"task1"<<std::endl;
}
void task2()
{
    std::cout<<"task2"<<std::endl;
}

void task3()
{
    std::cout<<"task3"<<std::endl;
}

void task4()
{
    std::cout<<"task4"<<std::endl;
}

void task5()
{
    std::cout<<"task5"<<std::endl;
}



void loadtask(std::vector<Task>&tasklist)  //准备任务
{
    tasklist.resize(5);
    tasklist[0]=task1;
    tasklist[1]=task2;
    tasklist[2]=task3;
    tasklist[3]=task4;
    tasklist[4]=task5;
}

void  CreatChildProc(std::vector<int>&  fddate,std::vector<pid_t>& childpid, std::vector<Task>const & tasklist)//创建子进程并连接管道，控制子进程逻辑
{
    int i=0;
    std::vector<int> deletefd;//保存后面创建的的子进程要关闭的文件描述符
    while(i<NUM)
    {
        int fd[2];
        int ret=pipe(fd); //创建管道
        assert(ret==0);
        (void)ret;
        //创建子进程
        pid_t id=fork();
        assert(id>=0);
        if(id==0)//子进程逻辑
        {
            while(1)
            {
                //首先就要关闭不属于自己的文件描述符
                for(auto closefd:deletefd)
                    close(closefd);
                close(fd[1]);
                int commandcode=0;
                int read_ret=read(fd[0],&commandcode,sizeof commandcode );
                if(read_ret==0)
                    break;
                assert(read_ret==4);
                std::cout <<"child is no."<<i+1 <<" the command code :"<<commandcode<<std::endl;//打印一行提示信息
                tasklist[commandcode-1]();//执行派发的任务
            }
            exit(0);
        }
        //父进程
        ++i;
        //关闭读端，保存写端 ，保存子进程id
        close(fd[0]);
        childpid.push_back(id);
        fddate.push_back(fd[1]);//这里的子进程 id 和 对应子进程的管道写入描述符是对应的，下标相同
        //更新 deletefd
        deletefd.push_back(fd[1]);
    }


}


void makeRandomSend()
{
    srand((unsigned int)time(NULL));
}


void sendCommand(std::vector<pid_t>const& fddate,std::vector<Task>const& tasklist,size_t count) //发送命令码
{
    while(count--)
    {
        int commandCode=random()%tasklist.size()+1; //对应任务的序号
        int pipeindex=random()%fddate.size();

        int wrret = write(fddate[pipeindex],&commandCode,sizeof commandCode);
        assert(wrret==4);
        (void)wrret;
        sleep(1);
    }
}


void WaitChildProc(std::vector<pid_t>& childpid,std::vector<int>&fddate)//回收子进程
{
    for(auto i=0;fddate.size();i++)
    {
        close(fddate[i]);
        pid_t id=waitpid(childpid[i],NULL,0);
        assert(id==childpid[i]);
        std::cout<<"回收"<<i<<"号进程"<<id<<"成功"<<std::endl;
    }
}



int main()
{

    makeRandomSend();
    std::vector<Task> tasklist;
    loadtask(tasklist);  //装载任务列表

    //创建子进程并且连接好管道
    std::vector<pid_t> childpid;
    std::vector<int> fddate;
    CreatChildProc(fddate,childpid,tasklist);
    
    sendCommand(fddate,tasklist,10);
    WaitChildProc(childpid,fddate);
}

